<?xml version="1.0" encoding="UTF-8"?>

<!-- 
        	Rules for Counter Reading Urban Dataset
        	Automatically generated from SCPS Ontology 2.0 on: 2026/04/27 11:49:07
-->

<schema queryBinding="xslt2" xmlns="http://purl.oclc.org/dsdl/schematron">
  <ns uri="smartcityplatform:enea:information:xml:schemas:main:urbandataset" prefix="scps"/>




  <pattern abstract="false" id="CounterReading">

    <let name="UDname" value="'Counter Reading'"/>
    <let name="UDuri" value="'https://smartcityplatform.enea.it/specification/semantic/2.0/ontology/scps-ontology-2.0.owl#CounterReading'"/>

    <let name="propertyNum" value="26"/>

    <let name="propertyDefNum" value="34"/>

    <let name="propNameList" value="'ElectricPanelID Line1Current Line2Current Line3Current PODID Phase1ActivePower Phase1ApparentPower Phase1PowerFactor Phase1ReactivePower Phase1Voltage Phase2ActivePower Phase2ApparentPower Phase2PowerFactor Phase2ReactivePower Phase2Voltage Phase3ActivePower Phase3ApparentPower Phase3PowerFactor Phase3ReactivePower Phase3Voltage TotalActiveEnergy TotalActivePower TotalApparentPower TotalReactiveEnergy TotalReactivePower TownCode coordinates period format height latitude longitude end_ts start_ts'"/>

    <let name="firstLevelPropNameList" value="'ElectricPanelID Line1Current Line2Current Line3Current PODID Phase1ActivePower Phase1ApparentPower Phase1PowerFactor Phase1ReactivePower Phase1Voltage Phase2ActivePower Phase2ApparentPower Phase2PowerFactor Phase2ReactivePower Phase2Voltage Phase3ActivePower Phase3ApparentPower Phase3PowerFactor Phase3ReactivePower Phase3Voltage TotalActiveEnergy TotalActivePower TotalApparentPower TotalReactiveEnergy TotalReactivePower TownCode'"/>

    <let name="propNameSet" value="tokenize('ElectricPanelID, Line1Current, Line2Current, Line3Current, PODID, Phase1ActivePower, Phase1ApparentPower, Phase1PowerFactor, Phase1ReactivePower, Phase1Voltage, Phase2ActivePower, Phase2ApparentPower, Phase2PowerFactor, Phase2ReactivePower, Phase2Voltage, Phase3ActivePower, Phase3ApparentPower, Phase3PowerFactor, Phase3ReactivePower, Phase3Voltage, TotalActiveEnergy, TotalActivePower, TotalApparentPower, TotalReactiveEnergy, TotalReactivePower, TownCode, coordinates, period, format, height, latitude, longitude, end_ts, start_ts', '[,\s]+')"/>

    <let name="dataTypeSet" value="tokenize('string, double, double, double, string, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, string, complex-4, complex-2, string, double, double, double, dateTime, dateTime', '[,\s]+')"/>

    <let name="umSet" value="tokenize('dimensionless, ampere, ampere, ampere, dimensionless, kilowatt, kilovoltampere, dimensionless, kilovoltamperereactive, volt, kilowatt, kilovoltampere, dimensionless, kilovoltamperereactive, volt, kilowatt, kilovoltampere, dimensionless, kilovoltamperereactive, volt, kilowattHour, kilowatt, kilovoltampere, kilovoltamperereactivePerHour, kilovoltamperereactive, dimensionless, dimensionless, dimensionless, dimensionless, dimensionless, dimensionless, dimensionless, dimensionless, dimensionless', '[,\s]+')"/>

    <let name="codeListSet" value="tokenize('null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, https://smartcityplatform.enea.it/specification/semantic/2.0/gc/TownCode.gc, null, null, https://smartcityplatform.enea.it/specification/semantic/2.0/gc/FormatCode.gc, null, null, null, null, null', '[,\s]+')"/>


    <let name="subPropNameSet" value="tokenize('coordinatesformat, coordinatesheight, coordinateslatitude, coordinateslongitude, periodend_ts, periodstart_ts', '[,\s]+')"/>


    <let name="root" value="scps:UrbanDataset"/>

    <let name="measurementType" value="tokenize('', '[,\s]+')"/>

    <let name="measurementTypeSet" value="tokenize('false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false', '[,\s]+')"/>


    <let name="propNameOcc" value="tokenize('1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1', '[,\s]+')"/>

    <let name="umAltSet" value="tokenize('', '[,\s]+')"/>


    <let name="mandatPropPosition" value="index-of($propNameOcc, '1')"/>
    <let name="mandatPropCount" value="count(index-of($propNameOcc, '1'))"/>
    <let name="firstID" value="//scps:line[1]/@id"/>
    
    <let name="useProfileProp"
      value="distinct-values(//scps:propertyDefinition[not(scps:propertyName = 'start_ts')][not(scps:propertyName = 'end_ts')][not(scps:propertyName = 'format')][not(scps:propertyName = 'longitude')][not(scps:propertyName = 'latitude')][not(scps:propertyName = 'height')][not(scps:propertyName = 'period')][not(scps:propertyName = 'coordinates')][not(scps:propertyName = 'DataDescription')][not(scps:propertyName = 'timestamp')]/scps:propertyName)"/>
    
    <let name="useProfilePropCount" value="count($useProfileProp)"/>
    
    <let name="coordinatesCheck"
      value="(count(//scps:propertyDefinition[scps:propertyName = 'coordinates']) = 1)"/>
    <let name="periodCheck"
      value="(count(//scps:propertyDefinition[scps:propertyName = 'period']) = 1)"/>
    <let name="descrCheck"
      value="(count(//scps:propertyDefinition[scps:propertyName = 'DataDescription']) = 1)"/>
    <let name="tsCheck"
      value="(count(//scps:propertyDefinition[scps:propertyName = 'timestamp']) = 1)"/>
    
    <let name="periodOcc" value="($propNameOcc[index-of($propNameSet, 'period')] = '1')"/>
    <let name="coordinatesOcc" value="($propNameOcc[index-of($propNameSet, 'coordinates')] = '1')"/>
    <let name="descrOcc" value="($propNameOcc[index-of($propNameSet, 'DataDescription')] = '1')"/>  
    <let name="tsOcc" value="($propNameOcc[index-of($propNameSet, 'timestamp')] = '1')"/>
    
    <rule context="//scps:specification">
      <assert test="not(@version) or (@version = '2.0')" flag="fatal" id="UD-001">[UD-001]- La
        versione delle specifiche ha un valore errato; il valore dovrebbe essere '2.0'.</assert>
      <assert test="(normalize-space(scps:name) = normalize-space($UDname))" flag="fatal"
        id="UD-002">[UD-002] - Il nome dell'UrbanDataset non e' coerente con la sua specifica; il
        valore dovrebbe essere <value-of select="$UDname"/>
      </assert>
      <assert test="(normalize-space(scps:uri) = normalize-space($UDuri))" flag="fatal" id="UD-003"
        >[UD-003] - Il riferimento alla specifica dell'UrbanDataset non e' corretto.</assert>
    </rule>
    
    <rule context="//scps:properties">
      <let name="propDefCount" value="count(scps:propertyDefinition)"/>
      <assert
        test="($propDefCount = count(distinct-values(scps:propertyDefinition/scps:propertyName)))"
        flag="fatal" id="UD-004">[UD-004]- Dichiaraziane di proprieta' ripetuta; ogni proprieta'
        puo' essere dichiarata una sola volta.</assert>
      
      <assert
        test="(($periodOcc) and ($coordinatesOcc) and ($propDefCount &lt;= $propertyDefNum) and ($propDefCount &gt;= count(index-of($propNameOcc, '1')))) or (not($periodOcc) and ($coordinatesOcc) and ($propDefCount &lt;= $propertyDefNum) and ($propDefCount &gt;= (count(index-of($propNameOcc, '1')) - 2))) or (($periodOcc) and not($coordinatesOcc) and ($propDefCount &lt;= $propertyDefNum) and ($propDefCount &gt;= (count(index-of($propNameOcc, '1')) - 2))) or (not($periodOcc) and not($coordinatesOcc) and ($propDefCount &lt;= $propertyDefNum) and ($propDefCount &gt;= (count(index-of($propNameOcc, '1')) - 4)))"
        flag="fatal" id="UD-013">[UD-013]- Il numero di proprieta' dichiarate non e' coerente con la
        specifica (proprietà dichiarate: <value-of select="count(scps:propertyDefinition)"/>;
        numero massimo di proprietà ammesse: <value-of select="$propertyDefNum"/>.</assert>
    </rule>
    
    <rule context="//scps:propertyDefinition">
      <let name="propDefPosition" value="count(preceding-sibling::scps:propertyDefinition) + 1"/>
      <let name="pos" value="index-of(($propNameSet), normalize-space(scps:propertyName))"/>
      <let name="subPrNum" value="$dataTypeSet[$pos]"/>
      <let name="prName" value="normalize-space(scps:propertyName)"/>
      <assert
        test="not(contains(normalize-space($subPrNum), 'complex-')) or (number(substring-after(normalize-space($subPrNum), 'complex-')) &gt;= count(child::scps:subProperties/scps:propertyName))"
        flag="fatal" id="UD-022">[UD-022]- L'elenco delle sottoproprieta' non e' coerente con la
        specifica.</assert>
      <assert test="not(empty(index-of(($propNameSet), $prName)))" flag="fatal" id="UD-005"
        >[UD-005]- La proprieta' dichiarata (<value-of select="scps:propertyName"/>) non appartiene
        all'UrbanDataset.</assert>
      <assert
        test="empty($pos) or ((scps:dataType) and (normalize-space(scps:dataType) = normalize-space($dataTypeSet[$pos]))) or ((contains(normalize-space($subPrNum), 'complex-')) and (count(//scps:subProperties/scps:propertyName) &gt; 0))"
        flag="fatal" id="UD-006">[UD-006]- La dichiarazione della proprieta' non e' coerente con la
        sua specifica (dataType errato o sottoproprieta' assenti).</assert>
      <assert
        test="empty($pos) or (contains(normalize-space($subPrNum), 'complex-')) or (normalize-space(scps:unitOfMeasure) = normalize-space($umSet[$pos])) or not(empty(index-of($umAltSet, concat(scps:propertyName, '_', normalize-space(scps:unitOfMeasure)))))"
        flag="fatal" id="UD-007">[UD-007]- L'unita' di misura dichiarata per la proprieta' <value-of
          select="$prName"/> non e' coerente con la sua specifica.</assert>
      <assert
        test="empty($pos) or (normalize-space($codeListSet[$pos]) = 'null') or (normalize-space(scps:codeList) = normalize-space($codeListSet[$pos]))"
        flag="fatal" id="UD-021">[UD-021]- La proprieta' dovrebbe essere associata ad una lista di
        codici: la dichirazione della lista di codici e' assente o non coerente con la
        specifica.</assert>
      <assert
        test="empty($pos) or ((scps:measurementType) and (normalize-space($measurementTypeSet[$pos]) = 'false')) or ((not(scps:measurementType) and normalize-space($measurementTypeSet[$pos]) = 'false') or ((scps:measurementType) and normalize-space($measurementTypeSet[$pos]) = 'true'))"
        flag="fatal" id="UD-030">[UD-030]- Occorre specificare il metodo di misurazione applicato
        per calcolare le misure relative alla proprieta' <value-of select="$prName"/>.</assert>
      <assert
        test="not(scps:measurementType) or not(empty(index-of($measurementType, scps:measurementType)))"
        flag="fatal" id="UD-031">[UD-031]- Il metodo di misurazione indicato per la proprieta'
        <value-of select="$prName"/> non e' ammesso.</assert>
      <assert
        test="empty($pos) or not((scps:measurementType) and (normalize-space($measurementTypeSet[$pos]) = 'false'))"
        flag="fatal" id="UD-032">[UD-032]- E' stato indicato il metodo di misurazione per una
        proprieta' (<value-of select="scps:propertyName"/>) per cui non e' previsto.</assert>
      <assert
        test="($propDefPosition &gt; $mandatPropCount) or (count((/scps:UrbanDataset//scps:propertyDefinition[scps:propertyName = $propNameSet[$mandatPropPosition[$propDefPosition]]])) &gt;= 1) or (($propNameSet[$mandatPropPosition[$propDefPosition]] = 'start_ts') and not($periodCheck)) or (($propNameSet[$mandatPropPosition[$propDefPosition]] = 'end_ts') and not($periodCheck)) or (($propNameSet[$mandatPropPosition[$propDefPosition]] = 'latitude') and not($coordinatesCheck)) or (($propNameSet[$mandatPropPosition[$propDefPosition]] = 'longitude') and not($coordinatesCheck))"
        flag="fatal" id="UD-037">[UD-037]- Tutte le proprietà obbligatorie per l'UD devono essere
        dichiarate nel blocco propertyDefinition; proprieta' <value-of
          select="$propNameSet[$mandatPropPosition[$propDefPosition]]"/> assente.</assert>
      <assert
        test="empty(index-of(($propNameSet), $prName)) or (not(empty(index-of(tokenize($firstLevelPropNameList, '[,\s]+'), $prName))) or ($prName = 'coordinates') or ($prName = 'period') or ($prName = 'DataDescription')) or (count(/scps:UrbanDataset//scps:subProperties[scps:propertyName = $prName]) &gt; 0)"
        flag="fatal" id="UD-044">[UD-044]- Sottoproprietà <value-of select="$prName"/> dichiarata ma
        non collegata a nessuna proprietà composta (tutte le sottoproprietà ammesse per
        l'UrbanDataset devono essere collegate ad una proprietà, coerentemente con la
        specifica).</assert>
    </rule>
    
    <rule context="//scps:subProperties/scps:propertyName">
      <let name="prName" value="."/>
      <let name="pr"
        value="concat(ancestor::scps:subProperties/preceding-sibling::scps:propertyName, .)"/>
      <assert
        test="not(empty(index-of($subPropNameSet, $pr))) or (empty(index-of($propNameSet, ancestor::scps:subProperties/preceding-sibling::scps:propertyName)))"
        flag="fatal" id="UD-011">[UD-011]- Dichiarazione di sottoproprieta' non coerente con la
        specifica.</assert>
      <assert
        test="(empty(index-of($subPropNameSet, $pr))) or (count(/scps:UrbanDataset//scps:propertyDefinition[scps:propertyName = $prName]) &gt; 0)"
        flag="fatal" id="UD-043">[UD-043]- Dichiarazione di sottoproprieta' assente (una proprietà è
        stata indicata nella lista delle sottoproprietà di un'altra proprietà, ma la sua
        dichiarazione non è stata fornita).</assert>
    </rule>
    
    <rule context="//scps:line">
      <assert test="(count(preceding-sibling::scps:line) + $firstID) = number(./@id)" flag="warning"
        id="UD-020">[UD-020]- Attenzione: l'indentificatore della linea non segue una corretta
        numerazione progressiva.</assert>
      <assert
        test="(($useProfilePropCount = 0) and count(descendant::scps:property) = $propertyNum) or (count(descendant::scps:property) = $useProfilePropCount)"
        flag="fatal" id="UD-008">[UD-008]- Il numero di proprieta' fornite nella linea <value-of
          select="position()"/> non e' coerente con il blocco 'specification': se il blocco
        'specification/properties' è assente, le linee dell'UrbanDataset devono riportare tutte le
        proprietà previste a livello di specifica, altrimenti solo quelle scelte per il Profilo
        d'uso, ovvero quelle dichiarate nel blocco 'specification/properties'.</assert>
      <assert test="count(scps:property/@name) = count(distinct-values(scps:property/@name))"
        flag="fatal" id="UD-015">[UD-015]- Proprieta' ripetuta.</assert>
      <assert
        test="(empty(index-of($propNameSet, 'coordinates'))) or (scps:coordinates) or (($propNameOcc[index-of($propNameSet, 'coordinates')] = '0') and not($coordinatesCheck))"
        flag="fatal" id="UD-017">[UD-017]- Blocco 'coordinates' assente nella linea <value-of
          select="position()"/>: il blocco deve essere presente in ogni linea.</assert>
      <assert
        test="(empty(index-of($propNameSet, 'period'))) or (scps:period) or (($propNameOcc[index-of($propNameSet, 'period')] = '0') and not($periodCheck))"
        flag="fatal" id="UD-018">[UD-018]- Blocco 'period' assente nella linea <value-of
          select="position()"/>: il blocco deve essere presente in ogni linea.</assert>
      <assert
        test="(empty(index-of($propNameSet, 'DataDescription'))) or (scps:description) or (($propNameOcc[index-of($propNameSet, 'DataDescription')] = '0') and not($descrCheck))"
        flag="fatal" id="UD-023">[UD-023]- Blocco 'description' assente nella linea <value-of
          select="position()"/>: il blocco deve essere presente in ogni linea.</assert>
      <assert
        test="(empty(index-of($propNameSet, 'timestamp'))) or (scps:timestamp) or (($propNameOcc[index-of($propNameSet, 'timestamp')] = '0') and not($tsCheck))"
        flag="fatal" id="UD-045">[UD-045]- Blocco 'timestamp' assente nella linea <value-of
          select="position()"/>: il blocco deve essere presente in ogni linea.</assert>
      
      <assert
        test="($coordinatesOcc) or (not(empty(index-of($propNameSet, 'coordinates'))) and ($coordinatesCheck)) or not(scps:coordinates)"
        flag="fatal" id="UD-034">[UD-034]- Blocco 'coordinates' rilevato nella linea <value-of
          select="position()"/>: il blocco non e' ammesso per questa istanza.</assert>
      <assert
        test="($periodOcc) or (not(empty(index-of($propNameSet, 'period'))) and ($periodCheck)) or not(scps:period)"
        flag="fatal" id="UD-035">[UD-035]- Blocco 'period' rilevato nella linea <value-of
          select="position()"/>: il blocco non e' ammesso per questa istanza.</assert>
      <assert
        test="($descrOcc) or (not(empty(index-of($propNameSet, 'DataDescription'))) and ($descrCheck)) or not(scps:description)"
        flag="fatal" id="UD-036">[UD-036]- Blocco 'description' rilevato nella linea <value-of
          select="position()"/>: il blocco non e' ammesso per questa istanza.</assert>
      <assert
        test="($tsOcc) or (not(empty(index-of($propNameSet, 'timestamp'))) and ($tsCheck)) or not(scps:timestamp)"
        flag="fatal" id="UD-046">[UD-046]- Blocco 'timestamp' rilevato nella linea <value-of
          select="position()"/>: il blocco non e' ammesso per questa istanza.</assert>
      
      <assert
        test="not($coordinatesCheck) or not(scps:coordinates) or (($useProfilePropCount = 0) and (scps:coordinates/scps:height)) or (not($useProfilePropCount = 0) and ((not(scps:coordinates/scps:height) and not(//scps:propertyDefinition/scps:propertyName = 'height')) or ((scps:coordinates/scps:height) and (//scps:propertyDefinition/scps:propertyName = 'height'))))"
        flag="fatal" id="UD-040">[UD-040]- Incoerenza sull'utilizzo della proprieta' 'height' a
        livello di linea: se non e' stato dichiarato un Profilo d'uso, la proprieta' deve essere
        presente nella linea; altrimenti il suo utilizzo nella linea deve essere coerente con quanto
        dichiarato nel blocco 'specification/properties'.</assert>
      
      <assert
        test="not($coordinatesCheck) or not(scps:coordinates) or (($useProfilePropCount = 0) and (scps:coordinates/@format)) or (not($useProfilePropCount = 0) and ((not(scps:coordinates/@format) and not(//scps:propertyDefinition/scps:propertyName = 'format')) or ((scps:coordinates/@format) and (//scps:propertyDefinition/scps:propertyName = 'format'))))"
        flag="fatal" id="UD-041">[UD-041]- Incoerenza sull'utilizzo della proprieta' 'format' a
        livello di linea: se non e' stato dichiarato un Profilo d'uso, la proprieta' deve essere
        presente nella linea; altrimenti il suo utilizzo nella linea deve essere coerente con quanto
        dichiarato nel blocco 'specification/properties'.</assert>
    </rule>
    
    <rule context="//scps:property">
      <let name="pos" value="index-of(($propNameSet), normalize-space(@name))"/>
      <let name="propName" value="(normalize-space(@name))"/>
      <let name="subPrNum" value="$dataTypeSet[$pos]"/>
      <assert test="not(empty(index-of(($propNameSet), $propName)))" flag="fatal"
        id="UD-009">[UD-009]- La proprieta' <value-of select="@name"/> non appartiene
        all'UrbanDataset.</assert>
      
      <assert
        test="(.[ancestor::scps:property]) or (contains($firstLevelPropNameList, $propName))"
        flag="fatal" id="UD-019">[UD-019]- La proprieta' <value-of select="@name"/> non e' stata
        fornita in modo coerente con la specifica.</assert>
      <assert
        test="empty($pos) or (not(contains(normalize-space($subPrNum), 'complex-')) and (scps:val)) or ((contains(normalize-space($subPrNum), 'complex-')) and (count(child::scps:property) > 0))"
        flag="fatal" id="UD-012">[UD-012]- Il valore della proprieta' <value-of select="$propName"/> non
        e' fornito in modo corretto.</assert>
      
      <assert
        test="not(.[ancestor::scps:property]) or not(empty(index-of($subPropNameSet, normalize-space(concat(ancestor::scps:property/@name, $propName)))))"
        flag="fatal" id="UD-014">[UD-014]- Il valore della proprieta' <value-of select="$propName"/> non
        e' fornito in modo corretto.</assert>
      
      <assert test="count(scps:property/@name) = count(distinct-values(scps:property/@name))"
        flag="fatal" id="UD-016">[UD-016]- Sottoproprieta' ripetuta.</assert>
      
      <assert
        test="($propNameOcc[index-of($propNameSet, $propName)] = '1') or ($useProfilePropCount = 0) or (not(empty(index-of($useProfileProp, $propName))))"
        flag="fatal" id="UD-042">[UD-042] - La proprieta' <value-of select="$propName"/> non e'
        ammessa nella riga o perche' è una proprieta' di contesto o perche' non e' stata dichiarata
        nella sezione propertyDefinition.</assert>
    </rule>
  </pattern>
</schema>
